
#pragma once

#include "tools/meta/is_complete.h"

#include <limits>

#if defined(__GNUC__) || defined(__MINGW32__) || defined(__MINGW__)
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Wenum-compare"
#endif

namespace tools
{
    namespace variant_detail
    {
//-----------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------

        template<int EnumID> struct Filter
        {
            typedef typename Enum2Type<EnumID>::Type Type;

            enum { eEXCLUDE =
                   !is_complete<Type>::value ||
                    Enum2Category<EnumID>::eCATEGORY == eKEY_VARIANT ||
                    WorkStatus<EnumID>::eSTATUS == DISABLED };
        };
        template<> struct Filter<eEND_TYPE>
        {
            enum { eEXCLUDE = 1 };
        };

        template<int EnumID = eNULL+1, bool exlcude = Filter<EnumID>::eEXCLUDE > struct Maxsize
        {
            enum { eSIZE = Maxsize<EnumID+1>::eSIZE };
        };
        template<> struct Maxsize<eEND_TYPE, true>
        {
            enum { eSIZE = 0 };
        };
        template<int EnumID> struct Maxsize<EnumID, false>
        {
            typedef typename Enum2Type<EnumID>::Type Type;
            enum { eSIZE1 = sizeof(Type)                   };
            enum { eSIZE2 = Maxsize<EnumID+1>::eSIZE       };
            enum { eSIZE  = eSIZE1>eSIZE2? eSIZE1 : eSIZE2 };
        };
        enum { eSIZE_BUFF = Maxsize<>::eSIZE };
//-----------------------------------------------------------------------------------------
#if 0
        template<class T> struct HasIterator
        {
            typedef char (&No)[1]; typedef char (&Yes)[2];
            template<class U>static Yes HelpMe( typename U::Iterator* p);
            template<class U>static No HelpMe(...);
            enum { value = sizeof(HelpMe<T>(0)) == sizeof(Yes) };
        };
        template<class T, int HAS_ITERATOR = HasIterator<T>::value> struct SizeIterator
        {
            typedef typename T::Iterator Iterator;
            enum { eSIZE = sizeof(Iterator) };
        };
        template<class T> struct SizeIterator<T,0>
        {
            enum { eSIZE = 0 };
        };

        template<class T, int IS_CONTAINER = Enum2Category< Type2Enum<T>::eTYPE >::eCATEGORY!=ePRIMITIVE>
        struct SizeStrategy
        { 
            enum { eSIZE = SizeIterator<T>::eSIZE }; 
        };
        template<class T> struct SizeStrategy<T,0>
        {
            enum { eSIZE = 0 };
        };
        template<int EnumID = eNULL+1> struct MaxIteratorSize
        {
            typedef typename Enum2Type<EnumID>::Type Type;
            enum { eSIZE1 = SizeStrategy<Type>::eSIZE         };
            enum { eSIZE2 = MaxIteratorSize<EnumID+1>::eSIZE  };
            enum { eSIZE  = eSIZE1>eSIZE2? eSIZE1 : eSIZE2    };
        };
        template<> struct MaxIteratorSize<eEND_TYPE>
        {
            enum { eSIZE = 0 };
        };
        enum { eSIZE_BUFF_ITERATORS = MaxIteratorSize<>::eSIZE };
#endif
//-----------------------------------------------------------------------------------------
        template<class T> struct PropertyType
        {
            enum { eLINK_STRING  = ::std::is_same<T, const char*>::value    || ::std::is_same<T, V::Str>::value  };
            enum { eLINK_WSTRING = ::std::is_same<T, const wchar_t*>::value || ::std::is_same<T, V::WStr>::value };

            typedef typename ::std::conditional<eLINK_STRING, V::Str,
                typename ::std::conditional<eLINK_WSTRING, V::WStr, T>::type
            >::type Type;

            static_assert( Type2Enum<Type>::eVALID, "THIS TYPE NOT SUPPORT" );

            enum { eIS_INTEGER = ::std::numeric_limits<T>::is_integer };
            enum { eIS_FLOAT   = ::std::is_floating_point<T>::value   };
            enum { eIS_BOOL    = ::std::is_same<T,bool>::value        };
            enum { eIS_INT     = eIS_INTEGER && !eIS_BOOL             };
            enum { eIS_STRICT  = !eIS_FLOAT && !eIS_INT && Type2Enum< Type >::eTYPE!=eNULL };
            enum { eIS_OTHER   = !eIS_FLOAT && !eIS_INT && !eIS_STRICT };
            enum { eIS_SIGNED  = ::std::is_signed<T>::value };

            enum { eSIGNED_INTEGER   = eIS_INT && eIS_SIGNED  };
            enum { eUNSIGNED_INTEGER = eIS_INT && !eIS_SIGNED };

            enum { eIS_BANNED  = WorkStatus< Type2Enum<Type>::eTYPE >::eSTATUS==DISABLED };
        };

//-----------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------
        template<class U, int CATEGORY = Enum2Category< Type2Enum<U>::eTYPE >::eCATEGORY> struct HelperCompare
        {
            typedef typename PropertyType<U>::Type Type;
            static bool IsValueSame(const void* _my, const void* _other)
            {
                const Type& my = *reinterpret_cast<const Type*>(_my);
                const Type& other = *reinterpret_cast<const Type*>(_other);

                if(my.size()!=other.size())
                    return false;

                return ::std::equal(  my.begin(), my.end(), other.begin() );
            }
        };
        template<class U> struct HelperCompare<U, eASSOCIATIVE>
        {
            typedef typename PropertyType<U>::Type Type;
            typedef typename Type::value_type ValueType;

            static bool IsValueSame(const void* _my, const void* _other)
            {
                const Type& my    = *reinterpret_cast<const Type*>(_my);
                const Type& other = *reinterpret_cast<const Type*>(_other);

                if(my.size()!=other.size())
                    return false;

                auto lambda = [](const ValueType& v1,const ValueType& v2)
                {
                    return (v1.first == v2.first) && (v1.second == v2.second);
                };

                return ::std::equal(  my.begin(), my.end(), other.begin(), lambda );
            }
        };

        template<class U> struct HelperCompare<U, ePRIMITIVE>
        {
            typedef typename PropertyType<U>::Type Type;
            static bool IsValueSame(const void* _my, const void* _other)
            {
                const Type& my = *reinterpret_cast<const Type*>(_my);
                const Type& other = *reinterpret_cast<const Type*>(_other);
                return my == other;
            }
        };
        template<class U> struct HelperCompare<U, eKEY_VARIANT>
        {
            typedef typename PropertyType<U>::Type Type;
            static bool IsValueSame(const void* _my, const void* _other)
            {
                const Type& keyval = *reinterpret_cast<const Type*>(_my);
                const Type& other = *reinterpret_cast<const Type*>(_other);
                return keyval.second == other.second;
            }
        };
//-----------------------------------------------------------------------------------------
        template<class T, int IS_CONTAINER = Enum2Category< Type2Enum<T>::eTYPE >::eCATEGORY > struct GetElements
        {
            static size_t Elements(const void* const value)
            {
                const T& v = *static_cast<const T*const >(value);
                return v.size();
            }
        };
        template<class T> struct GetElements<T, ePRIMITIVE>
        {
            static size_t Elements(const void* const ){ return 0; }
        };
        template<class T> struct GetElements<T, eKEY_VARIANT>
        {
            static size_t Elements(const void* const ){ return 0; }
        };


//-----------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------


    }//namespace value_detail
}//namespace tools

#if defined(__GNUC__) || defined(__MINGW32__) || defined(__GNUC__)
    #pragma GCC diagnostic pop
#endif
